#include "napi/native_api.h"
#include "../common/common.h"
#include "hilog/log.h"
#include "string.h"
#include <js_native_api_types.h>

char value[256] = "";
size_t value_length;
char newValue[13] = "new username";

static napi_value Add(napi_env env, napi_callback_info info)
{
    size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);

    double value0;
    napi_get_value_double(env, args[0], &value0);

    double value1;
    napi_get_value_double(env, args[1], &value1);

    napi_value sum;
    napi_create_double(env, value0 + value1, &sum);
    
    return sum;

}

// goal: 获取JS侧对象的属性(传入了键名)，赋值操作后再返回到JS
static napi_value Get(napi_env env, napi_callback_info info) {
  OH_LOG_INFO(LOG_APP, "in Get__________________");  
  size_t argc = 2;
  napi_value args[2];

  NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

  NODE_API_ASSERT(env, argc >= 2, "Wrong number of arguments");

  napi_valuetype valuetype0;
    //获取参数0， object
  NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));

  NODE_API_ASSERT(env, valuetype0 == napi_object,
      "Wrong type of arguments. Expects an object as first argument.");
    //获取参数1， key
  napi_valuetype valuetype1;
  NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));
    
  NODE_API_ASSERT(env, valuetype1 == napi_string || valuetype1 == napi_symbol,
      "Wrong type of arguments. Expects a string or symbol as second.");

  napi_value object = args[0];
  napi_value output;
    
    //object 中key对应的值
  NODE_API_CALL(env, napi_get_property(env, object, args[1], &output));

  NODE_API_CALL(env,napi_get_value_string_utf8(env, output, value, 255, &value_length));   // 这里C++侧获取到了JS侧的键值，传入函数名的话无法获取对应的string

  OH_LOG_INFO(LOG_APP, "C++ get user name before modify: %{public}s", value);
    
    //拷贝值
  strcpy(value, newValue);  // 这里操作获取到的JS键值，操作后创建napi_value，再将new_val赋值给JS的键值

  napi_value new_val; 
  NODE_API_CALL(env, napi_create_string_utf8(env, value, NAPI_AUTO_LENGTH, &new_val));
  NODE_API_CALL(env, napi_set_property(env, args[0], args[1], new_val));  

  return new_val;
}

// 传入实例对象，在C++侧调用对象中的函数
static napi_value CallFunction(napi_env env, napi_callback_info info) {
  OH_LOG_INFO(LOG_APP, "in CallFunction__________________");  
  size_t argc = 1;
  napi_value args[1];

  NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

  NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");

  napi_valuetype valuetype0;
  NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));

  NODE_API_ASSERT(env, valuetype0 == napi_object,
      "Wrong type of arguments. Expects an object as first argument.");
    
  napi_value oncall; 
  napi_status status = napi_get_named_property(env, args[0], "oncall", &oncall);
  if (status != napi_ok) return oncall;
  napi_value return_val;
  OH_LOG_INFO(LOG_APP, "C++ call function in user");  
  status = napi_call_function(env, args[0], oncall, 0, NULL, &return_val);
  return oncall;
}

// goal: object in object，对内部的object属性赋值，再返回给JS侧
static napi_value GetObjInObj(napi_env env, napi_callback_info info) {
  OH_LOG_INFO(LOG_APP, "in GetObjInObj__________________");  
  size_t argc = 1;
  napi_value args[1];

  NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

  NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
    
    //获取第0个参数
  napi_valuetype valuetype0;
  NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));

  NODE_API_ASSERT(env, valuetype0 == napi_object,
      "Wrong type of arguments. Expects an object as first argument.");

  //获取参数中的+  
  napi_value output;
  NODE_API_CALL(env, napi_get_named_property(env, args[0], "location", &output));

  napi_valuetype valuetype1;
  NODE_API_CALL(env, napi_typeof(env, output, &valuetype1));  
  NODE_API_ASSERT(env, valuetype1 == napi_object,
       "Wrong type of arguments. Expects an object as first argument."); 
    
  napi_value country;
  NODE_API_CALL(env, napi_get_named_property(env, output, "country", &country)); 
    
  napi_valuetype valuetype2;
  NODE_API_CALL(env, napi_typeof(env, country, &valuetype2));  
  NODE_API_ASSERT(env, valuetype2 == napi_string,
         "Wrong type of arguments. Expects an object as first argument.");  
  
  NODE_API_CALL(env, napi_get_value_string_utf8(env, country, value, 255, &value_length));   // 这里C++侧获取到了JS侧的键值，传入函数名的话无法获取对应的string
  
  OH_LOG_INFO(LOG_APP, "C++ get user country before modify: %{public}s", value);
      
  strcpy(value, newValue);  // 这里操作获取到的JS键值，操作后创建napi_value，再将new_val赋值给JS的键值
  
  napi_value new_val;  
  NODE_API_CALL(env, napi_create_string_utf8(env, value, NAPI_AUTO_LENGTH, &new_val));
  NODE_API_CALL(env, napi_set_named_property(env, output, "country", new_val));  
  
  return new_val;  
}

static napi_value GetNamed(napi_env env, napi_callback_info info) {
  size_t argc = 2;
  napi_value args[2];
  char key[256] = "";
  size_t key_length;
  NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

  NODE_API_ASSERT(env, argc >= 2, "Wrong number of arguments");

  napi_valuetype value_type0;
  NODE_API_CALL(env, napi_typeof(env, args[0], &value_type0));

  NODE_API_ASSERT(env, value_type0 == napi_object,
      "Wrong type of arguments. Expects an object as first argument.");

  napi_valuetype value_type1;
  NODE_API_CALL(env, napi_typeof(env, args[1], &value_type1));

  NODE_API_ASSERT(env, value_type1 == napi_string,
      "Wrong type of arguments. Expects a string as second.");

  napi_value object = args[0];
  NODE_API_CALL(env,
      napi_get_value_string_utf8(env, args[1], key, 255, &key_length));
  key[255] = 0;
  NODE_API_ASSERT(env, key_length <= 255,
      "Cannot accommodate keys longer than 255 bytes");
  napi_value output;
  NODE_API_CALL(env, napi_get_named_property(env, object, key, &output));

  return output;
}


static napi_value GetPropertyNames(napi_env env, napi_callback_info info) {
  size_t argc = 1;
  napi_value args[1];
  NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

  NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");

  napi_valuetype value_type0;
  NODE_API_CALL(env, napi_typeof(env, args[0], &value_type0));

  NODE_API_ASSERT(env, value_type0 == napi_object,
      "Wrong type of arguments. Expects an object as first argument.");

  napi_value output;
  NODE_API_CALL(env, napi_get_property_names(env, args[0], &output));
    
//  napi_value second;  
//  NODE_API_CALL(env, napi_get_element(env, output, 1, &second));  // 获取指定index的键 

  return output;
}

static napi_value Set(napi_env env, napi_callback_info info) {
  size_t argc = 3;
  napi_value args[3];
  NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

  NODE_API_ASSERT(env, argc >= 3, "Wrong number of arguments");

  napi_valuetype valuetype0;
  NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));

  NODE_API_ASSERT(env, valuetype0 == napi_object,
      "Wrong type of arguments. Expects an object as first argument.");

  napi_valuetype valuetype1;
  NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));

  NODE_API_ASSERT(env, valuetype1 == napi_string || valuetype1 == napi_symbol,
      "Wrong type of arguments. Expects a string or symbol as second.");

  NODE_API_CALL(env, napi_set_property(env, args[0], args[1], args[2]));

  napi_value valuetrue;
  NODE_API_CALL(env, napi_get_boolean(env, true, &valuetrue));

  return valuetrue;
}

static napi_value SetNamed(napi_env env, napi_callback_info info) {
  size_t argc = 3;
  napi_value args[3];
  char key[256] = "";
  size_t key_length;
  NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

  NODE_API_ASSERT(env, argc >= 3, "Wrong number of arguments");

  napi_valuetype value_type0;
  NODE_API_CALL(env, napi_typeof(env, args[0], &value_type0));

  NODE_API_ASSERT(env, value_type0 == napi_object,
      "Wrong type of arguments. Expects an object as first argument.");

  napi_valuetype value_type1;
  NODE_API_CALL(env, napi_typeof(env, args[1], &value_type1));

  NODE_API_ASSERT(env, value_type1 == napi_string,
      "Wrong type of arguments. Expects a string as second.");

  NODE_API_CALL(env,
      napi_get_value_string_utf8(env, args[1], key, 255, &key_length));
  key[255] = 0;
  NODE_API_ASSERT(env, key_length <= 255,
      "Cannot accommodate keys longer than 255 bytes");

  NODE_API_CALL(env, napi_set_named_property(env, args[0], key, args[2]));

  napi_value value_true;
  NODE_API_CALL(env, napi_get_boolean(env, true, &value_true));

  return value_true;
}

static napi_value Has(napi_env env, napi_callback_info info) {
  size_t argc = 2;
  napi_value args[2];
  NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

  NODE_API_ASSERT(env, argc >= 2, "Wrong number of arguments");

  napi_valuetype valuetype0;
  NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));

  NODE_API_ASSERT(env, valuetype0 == napi_object,
      "Wrong type of arguments. Expects an object as first argument.");

  napi_valuetype valuetype1;
  NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));

  NODE_API_ASSERT(env, valuetype1 == napi_string || valuetype1 == napi_symbol,
      "Wrong type of arguments. Expects a string or symbol as second.");

  bool has_property;
  NODE_API_CALL(env, napi_has_property(env, args[0], args[1], &has_property));

  napi_value ret;
  NODE_API_CALL(env, napi_get_boolean(env, has_property, &ret));

  return ret;
}

static napi_value HasNamed(napi_env env, napi_callback_info info) {
  size_t argc = 2;
  napi_value args[2];
  char key[256] = "";
  size_t key_length;
  NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

  NODE_API_ASSERT(env, argc >= 2, "Wrong number of arguments");

  napi_valuetype value_type0;
  NODE_API_CALL(env, napi_typeof(env, args[0], &value_type0));

  NODE_API_ASSERT(env, value_type0 == napi_object,
      "Wrong type of arguments. Expects an object as first argument.");

  napi_valuetype value_type1;
  NODE_API_CALL(env, napi_typeof(env, args[1], &value_type1));

  NODE_API_ASSERT(env, value_type1 == napi_string || value_type1 == napi_symbol,
      "Wrong type of arguments. Expects a string as second.");

  NODE_API_CALL(env,
      napi_get_value_string_utf8(env, args[1], key, 255, &key_length));
  key[255] = 0;
  NODE_API_ASSERT(env, key_length <= 255,
      "Cannot accommodate keys longer than 255 bytes");

  bool has_property;
  NODE_API_CALL(env, napi_has_named_property(env, args[0], key, &has_property));

  napi_value ret;
  NODE_API_CALL(env, napi_get_boolean(env, has_property, &ret));

  return ret;
}
static napi_value Delete(napi_env env, napi_callback_info info) {
  size_t argc = 2;
  napi_value args[2];

  NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
  NODE_API_ASSERT(env, argc == 2, "Wrong number of arguments");

  napi_valuetype valuetype0;
  NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
  NODE_API_ASSERT(env, valuetype0 == napi_object,
      "Wrong type of arguments. Expects an object as first argument.");

  napi_valuetype valuetype1;
  NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));
  NODE_API_ASSERT(env, valuetype1 == napi_string || valuetype1 == napi_symbol,
      "Wrong type of arguments. Expects a string or symbol as second.");

  bool result;
  napi_value ret;
  NODE_API_CALL(env, napi_delete_property(env, args[0], args[1], &result));
  NODE_API_CALL(env, napi_get_boolean(env, result, &ret));
    
  OH_LOG_INFO(LOG_APP, "in CallFunction Delete result=%{public}d", result);

  return ret;
}

static napi_value New(napi_env env, napi_callback_info info) {
  napi_value ret;
  NODE_API_CALL(env, napi_create_object(env, &ret));

  napi_value num;
  NODE_API_CALL(env, napi_create_int32(env, 987654321, &num));

  NODE_API_CALL(env, napi_set_named_property(env, ret, "test_number", num));

  napi_value str;
  const char* str_val = "test string";
  size_t str_len = strlen(str_val);
  NODE_API_CALL(env, napi_create_string_utf8(env, str_val, str_len, &str));

  NODE_API_CALL(env, napi_set_named_property(env, ret, "test_string", str));

  return ret;
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
    { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr },
    { "Get", nullptr, Get, nullptr, nullptr, nullptr, napi_default, nullptr },
    { "CallFunction", nullptr, CallFunction, nullptr, nullptr, nullptr, napi_default, nullptr },
    { "GetObjInObj", nullptr, GetObjInObj, nullptr, nullptr, nullptr, napi_default, nullptr },
    { "GetNamed", nullptr, GetNamed, nullptr, nullptr, nullptr, napi_default, nullptr },
    { "GetPropertyNames", nullptr, GetPropertyNames, nullptr, nullptr, nullptr, napi_default, nullptr },
    { "Set", nullptr, Set, nullptr, nullptr, nullptr, napi_default, nullptr },
    { "SetNamed", nullptr, SetNamed, nullptr, nullptr, nullptr, napi_default, nullptr },
    { "Has", nullptr, Has, nullptr, nullptr, nullptr, napi_default, nullptr },
    { "HasNamed", nullptr, HasNamed, nullptr, nullptr, nullptr, napi_default, nullptr },
    { "Delete", nullptr, Delete, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version =1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
    napi_module_register(&demoModule);
}